import { GlFormCheckbox, GlFormGroup, GlFormCheckboxGroup } from '@gitlab/ui';
import { nextTick } from 'vue';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import ChecklistWidget from '~/pipeline_wizard/components/widgets/checklist.vue';

describe('Pipeline Wizard - Checklist Widget', () => {
  let wrapper;
  const props = {
    title: 'Foobar',
    items: [
      'foo bar baz', // simple, text-only content
      {
        text: 'abc',
        help: 'def',
      },
    ],
  };

  const getLastUpdateValidEvent = () => {
    const eventArray = wrapper.emitted('update:valid');
    return eventArray[eventArray.length - 1];
  };
  const findItem = (atIndex = 0) => wrapper.findAllComponents(GlFormCheckbox).at(atIndex);
  const getGlFormGroup = () => wrapper.getComponent(GlFormGroup);
  const getGlFormCheckboxGroup = () => wrapper.getComponent(GlFormCheckboxGroup);

  // The item.ids *can* be passed inside props.items, but are usually
  // autogenerated by lodash.uniqueId() inside the component. So to
  // get the actual value that the component expects to be emitted in
  // GlFormCheckboxGroup's `v-model`, we need to obtain the value that is
  // actually passed to GlFormCheckbox.
  const getAllItemIds = () => props.items.map((_, i) => findItem(i).attributes().value);

  const createComponent = (mountFn = shallowMountExtended) => {
    wrapper = mountFn(ChecklistWidget, {
      propsData: {
        ...props,
      },
    });
  };

  it('creates the component', () => {
    createComponent();
    expect(wrapper.exists()).toBe(true);
  });

  it('displays the item', () => {
    createComponent();
    expect(findItem().exists()).toBe(true);
  });

  it("displays the item's text", () => {
    createComponent();
    expect(findItem().text()).toBe(props.items[0]);
  });

  it('assigns the same non-null value to label-for and form id', () => {
    createComponent();
    const formGroupLabelFor = getGlFormGroup().attributes('label-for');
    const formCheckboxGroupId = getGlFormCheckboxGroup().attributes('id');

    expect(formGroupLabelFor).not.toBeNull();
    expect(formCheckboxGroupId).not.toBeNull();
    expect(formGroupLabelFor).toBe(formCheckboxGroupId);
  });

  it('displays an item with a help text', () => {
    createComponent();
    const { text, help } = props.items[1];

    const itemWrapper = findItem(1);
    const itemText = itemWrapper.text();
    // Unfortunately there is no wrapper.slots() accessor in vue_test_utils.
    // To make sure the help text is being passed to the correct slot, we need to
    // access the slot internally.
    // This selector accesses the text of the first slot named "help" in itemWrapper
    const helpText = itemWrapper.vm.$slots?.help[0]?.text?.trim();

    expect(itemText).toBe(text);
    expect(helpText).toBe(help);
  });

  it("emits a 'update:valid' event after all boxes have been checked", async () => {
    createComponent();
    // initially, `valid` should be false
    expect(wrapper.emitted('update:valid')).toEqual([[false]]);
    const values = getAllItemIds();
    // this mocks checking all the boxes
    getGlFormCheckboxGroup().vm.$emit('input', values);

    await nextTick();

    expect(wrapper.emitted('update:valid')).toEqual([[false], [true]]);
  });

  it('emits a invalid event after a box has been unchecked', async () => {
    createComponent();
    // initially, `valid` should be false
    expect(wrapper.emitted('update:valid')).toEqual([[false]]);

    // checking all the boxes first
    const values = getAllItemIds();
    getGlFormCheckboxGroup().vm.$emit('input', values);
    await nextTick();

    // ensure the test later doesn't just pass because it doesn't emit
    // `true` to begin with
    expect(getLastUpdateValidEvent()).toEqual([true]);

    // Now we're unchecking the last box.
    values.pop();
    getGlFormCheckboxGroup().vm.$emit('input', values);
    await nextTick();

    expect(getLastUpdateValidEvent()).toEqual([false]);
  });
});
